package model;


import com.ibm.staf.STAFException;
import com.ibm.staf.STAFHandle;
import com.ibm.staf.STAFMarshallingContext;
import com.ibm.staf.STAFResult;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author pyin
 */
public class VMMachine {

    private String os;
    private String name;
    private String vmHost;
    private String vmFileName;
    private String vmSnapShot;
    private String tsProjectPath;
    private String tsProjectArchive;

    /**
     * @return the os
     */
    public String getOs() {
        return os;
    }

    /**
     * @param os the os to set
     */
    public void setOs(String os) {
        this.os = os;
    }

    public void setTsProjectArchive(String name){
        this.tsProjectArchive = name;
    }

    public String getTsProjectArchive(){
        return this.tsProjectArchive;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the vmHost
     */
    public String getVmHost() {
        return vmHost;
    }

    /**
     * @param vmHost the vmHost to set
     */
    public void setVmHost(String vmHost) {
        this.vmHost = vmHost;
    }

    /**
     * @return the vmFileName
     */
    public String getVmFileName() {
        return vmFileName;
    }

    /**
     * @param vmFileName the vmFileName to set
     */
    public void setVmFileName(String vmFileName) {
        this.vmFileName = vmFileName;
    }

    /**
     * @return the vmSnapShot
     */
    public String getVmSnapShot() {
        return vmSnapShot;
    }

    /**
     * @param vmSnapShot the vmSnapShot to set
     */
    public void setVmSnapShot(String vmSnapShot) {
        this.vmSnapShot = vmSnapShot;
    }

    public boolean hasCurrentBuildSnapshot() throws BuildNameNotSetException{
        String buildName = ConfigManager.getBuildName();
        if(buildName==null){
            throw new BuildNameNotSetException();
        }else{
            String[] snapshots = this.getSnapshots();
            for(int i=0;i<snapshots.length;i++){
                if(snapshots[i].equals(buildName))
                    return true;
            }
            return false;
        }
    }

    public boolean isStarted() throws VMNotReadyException{
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.isStartedHandleName());
            STAFResult result = handle.submit2(this.name, "ping", "ping");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            }
            if (result.rc == 16)
                throw new VMNotReadyException();
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean isHostingMachineReady(){
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.isStartedHandleName());
            STAFResult result = handle.submit2(this.vmHost, "ping", "ping");
            handle.unRegister();
            if (result.rc == 0) {
                return true;
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public String[] getSnapshots() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getSnapshotsHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command" +
                    " \"vmrun listSnapshots \\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        String returned = item.get("data").toString();
//                        System.out.println(returned);
                        return parseReturnedResult(returned);
                    }
//                    Iterator it = hash.keySet().iterator();
//                    while(it.hasNext()){
//                        String key = (String)it.next();
//                        System.out.println("["+key+"]="+hash.get(key).toString());
//                    }
                }
            }
            else {
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public boolean revertToSnapshot(String snapshotName) throws VMNotReadyException{
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.revertToSnapshotHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command \"vmrun revertToSnapshot " +
                    "\\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\" " + this.vmSnapShot +
                    " \" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        return true;
                    }
                }
            }
            if( result.rc == 16){
                throw new VMNotReadyException();
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean start() {
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.startVMHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command \"vmrun start " +
                    "\\\"" + this.vmFileName.replace("\\", "\\\\") + "\\\"\" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        while(true){
                            if(isStarted()){
                                return true;
                            }else{
                                Thread.sleep(1000);
                                Logger.getLogger("Wait for VMMachine to start up...");
                                System.out.println("Wait for VMMachine to start up...");
                            }
                        }
                    }
                }
            }
        } catch (Exception ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }
//
//    public String getCurrentSnapShot(){
//        try {
//            STAFHandle handle = new STAFHandle("");
//            handle.submit2(os, os, os)
//        } catch (STAFException ex) {
//            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
//        }
//
//    }
//
    public boolean setupEnv(String buildName)throws BuildNameNotSetException{
        return createServerSideProjectArchive()&&getProjectArchive()&&extractProjectArchive()
                &&stop()&&takeSnapshot(buildName);
    }

    public boolean stop(){
       try {
            STAFHandle handle = new STAFHandle(ConfigManager.getStopVMHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command  stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        return true;
                    }
                }
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean takeSnapshot(String name)throws BuildNameNotSetException {
        try {
            if(name==null)
                throw new BuildNameNotSetException();
            STAFHandle handle = new STAFHandle(ConfigManager.getTakeSnapshotHandleName());
            STAFResult result = handle.submit2(this.vmHost, "process", "start command vmrun snapshot \""+this.getVmFileName()+"\" "+ConfigManager.getBuildName()+" stderrtostdout returnstdout wait");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        return true;
                    }
                }
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    private boolean createServerSideProjectArchive(){
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getZipHandleName());
            STAFResult result = handle.submit2(ConfigManager.getServerHostName(), "zip", "add zipfile "+"\""+ConfigManager.getProjectArchiveName().replace("\\", "/")+
                    "\" directory \""+ConfigManager.getProjectPath().replace("\\", "/") + "\" relativeto \""+ConfigManager.getProjectPath().replace("\\", "/")+"\" recurse");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        return true;
                    }
                }
            }else{
                System.out.println(result.result);
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;

    }

    private boolean getProjectArchive(){
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getFSHandleName());
            STAFResult result = handle.submit2(this.vmHost, "fs", "copy \""+ConfigManager.getProjectArchiveName()+"\" tomachine "+this.name+" tofile \""+this.tsProjectArchive+"\"");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        return true;
                    }
                }
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;

    }

    private boolean extractProjectArchive(){
        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getUnzipHandleName());
            STAFResult result = handle.submit2(this.vmHost, "zip", "unzip zipfile \""+this.tsProjectArchive+"\" todirectory \""+this.tsProjectPath+"\"");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    HashMap hash = (HashMap) mc.getRootObject();
                    HashMap item = (HashMap) ((LinkedList) hash.get("fileList")).get(0);
                    if (item.get("rc").toString().equals("0")) {
                        return true;
                    }
                }
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    public boolean restore(String snapshotName) throws VMNotReadyException{
        return (revertToSnapshot(snapshotName)&&start());
    }



    public static void main(String[] args){
        try {
            VMMachine machine = VMMachineFactory.getVMMachine("win03uepinstall");
            machine.createServerSideProjectArchive();
//            System.out.println(machine.getOs());
        } catch (InvalidVMHostNameException e){
            System.out.println(e.getMessage());
        }

    }

    public void setTsProjectPath(String path) {
        this.tsProjectPath = path;
    }

    public String getTsProjectPath(){
        return this.tsProjectPath;
    }

    private String[] parseReturnedResult(String returned) {
        String[] result = returned.split("\n");
        String[] x = new String[result.length - 1];
        for (int i = 0; i < x.length; i++) {
            x[i] = result[i + 1];
        }
        return x;
    }


    public TestScript getRunningScript() throws VMNotReadyException, InvalidTSException, InvalidVMHostNameException{

        try {
            STAFHandle handle = new STAFHandle(ConfigManager.getListHandlesHandleName());
            STAFResult result = handle.submit2(this.vmHost, "handle", "list handles");
            handle.unRegister();
            if (result.rc == 0) {
                if (STAFMarshallingContext.isMarshalledData(result.result)) {
                    STAFMarshallingContext mc = STAFMarshallingContext.unmarshall(
                            result.result);
                    LinkedList list = (LinkedList) mc.getRootObject();
                    Iterator it = list.iterator();
                    while(it.hasNext()){
                        HashMap entry = (HashMap)it.next();
//                        System.out.println(entry.get("name"));
                        String name = entry.get("name").toString();
                        if(name.startsWith("TSH_")){
                            return TSManager.getScript(name.replace("TSH_", "").trim());
                        }
                    }
                }
            }
            if ( result.rc == 16 ){
                throw new InvalidTSException();
            }
        } catch (STAFException ex) {
            Logger.getLogger(VMMachine.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

}
